﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

using ModbusCS;

namespace Test2
{
    class CommHelper
    {
        public class CmdUnit
        {
            public string IDCType = "D";
            public ushort Addr = 0;
            public ushort Count = 0;
            public ushort[] Data = null;

            public CmdUnit(string idcType, ushort addr, ushort count)
            {
                IDCType = idcType;
                Addr = addr;
                Count = count;
                Data = new ushort[count];

            }
        }
        #region============================== 变量 ====================================
        public delegate void OnErrorMsgEventHandler(string msg);
        public event OnErrorMsgEventHandler OnErrorMsg;

        public delegate void OnUploadForLogEventHandler(byte[] datas);
        public event OnUploadForLogEventHandler OnUploadForLog;

        //public ushort[] EmData = new ushort[65535];
        public List<CommDataUnit> RegDataBuff = new List<CommDataUnit>();

        public Dictionary<ushort, CmdUnit> L1Cmds = new Dictionary<ushort, CmdUnit>();
        public Dictionary<ushort, CmdUnit> L2Cmds = new Dictionary<ushort, CmdUnit>();
        public Dictionary<ushort, CmdUnit> L3Cmds = new Dictionary<ushort, CmdUnit>();
        public ConcurrentQueue<CommandUnit> CmdQ = new ConcurrentQueue<CommandUnit>();


        Thread thr = null;


        IntPtr plc_handle = IntPtr.Zero;
        string m_ip = "192.168.172.126";
        ushort m_port = 502;
        string m_plcType = "5000 Series";
        bool m_isConnected = false;
        bool m_start = false;


        #endregion=====================================================================


        #region============================== 属性 ====================================
        public bool IsConnected { get { return m_isConnected; } }
        #endregion=====================================================================


        #region============================= 内部函数 ================================
        private void Init()
        {
            CreateFixCmdList();

            for (int i = 0; i < 3000; i++)
            {
                RegDataBuff.Add(new CommDataUnit(RegDataBuff.Count + 1 + CommDataUnit.AddrHead));
            }

            bool ret = ModbusSdk.ModbusCreate(m_ip, m_port, m_plcType, ref plc_handle);
            if (!ret)
            {
                OnErrorMsg?.Invoke("modbus instant error");

            }
            else
            {
                thr = new Thread(new ThreadStart(ReadingProcess));
                thr.IsBackground = true;
            }

        }

        //固定读取队列
        private void CreateFixCmdList()
        {
            //加入数据读取指令

            //PV读取最高级
            L1Cmds.Add(15000, new CmdUnit("D", 15000, 125));
            L1Cmds.Add(15125, new CmdUnit("D", 15125, 125));
            L1Cmds.Add(15250, new CmdUnit("D", 15250, 125));
            L1Cmds.Add(15375, new CmdUnit("D", 15375, 125));
            L1Cmds.Add(15500, new CmdUnit("D", 15500, 125));
            L1Cmds.Add(15625, new CmdUnit("D", 15625, 125));
            L1Cmds.Add(15750, new CmdUnit("D", 15750, 125));
            L1Cmds.Add(15875, new CmdUnit("D", 15875, 125));

            L1Cmds.Add(16000, new CmdUnit("D", 16000, 125));
            L1Cmds.Add(16125, new CmdUnit("D", 16125, 125));



            //SV读取低一级
            L2Cmds.Add(13000, new CmdUnit("D", 13000, 125));
            L2Cmds.Add(13125, new CmdUnit("D", 13125, 125));
            L2Cmds.Add(13250, new CmdUnit("D", 13250, 125));
            L2Cmds.Add(13375, new CmdUnit("D", 13375, 125));
            L2Cmds.Add(13500, new CmdUnit("D", 13500, 125));

            //输入输出低两级
            L3Cmds.Add(17000, new CmdUnit("D", 17000, 125));
        }

        #endregion====================================================================


        #region============================= 保护函数 ================================
        private void CheckConnection()
        {
            if (ModbusSdk.ModbusIsConnect(plc_handle) == false)
            {
                if (ModbusSdk.ModbusConnect(plc_handle) == true)
                {
                    m_isConnected = true;
                }
                else
                {
                    m_isConnected = false;
                    Thread.Sleep(3000);
                }
            }


        }

        private void UpdateRegDataBuff(ushort[] dataBuff)
        {
            for (int i = 0; i < dataBuff.Length; i++)
            {
                RegDataBuff[i].UpdateData(dataBuff[i]);
            }
        }

        private void ReadingProcess()
        {
            while (true)
            {
                CheckConnection();

                try
                {
                    //for (ushort i = 0; i < L1Cmds.Count; i++)
                    //{
                    //    //int rs = ModbusSdk.ModbusReadHoldingRegisters(plc_handle, "D", 13000, 125, new ushort[125]);



                    //    string _idcType = L1Cmds[i].IDCType;
                    //    ushort _addr = L1Cmds[i].Addr;
                    //    ushort _length = L1Cmds[i].Count;
                    //    ushort[] _data = new ushort[125];

                    //    //int rs = ModbusSdk.ModbusReadHoldingRegisters(plc_handle, _idcType, _addr, _length, _data);
                    //    int rs = ModbusSdk.ModbusReadHoldingRegisters(plc_handle, L1Cmds[i].IDCType, L1Cmds[i].Addr, 125, L1Cmds[i].Data);
                    //    if (rs == 0)
                    //    {
                    //        UpdateRegDataBuff(L1Cmds[i].Data);
                    //    }
                    //    else
                    //    {
                    //        break;
                    //    }
                    //}

                    List<ushort> keys = L1Cmds.Keys.ToList();
                    for (ushort i = 0; i < keys.Count; i++)
                    {
                        //int rs = ModbusSdk.ModbusReadHoldingRegisters(plc_handle, "D", 13000, 1, new ushort[] { 1 });

                        int rs = ModbusSdk.ModbusReadHoldingRegisters(plc_handle, L1Cmds[keys[i]].IDCType, L1Cmds[keys[i]].Addr, 125, L1Cmds[keys[i]].Data);
                        if (rs == 0)
                        {
                            UpdateRegDataBuff(L1Cmds[keys[i]].Data);
                        }
                        else
                        {
                            break;
                        }
                    }

                    keys = L2Cmds.Keys.ToList();
                    for (ushort i = 0; i < keys.Count; i++)
                    {
                        int rs = ModbusSdk.ModbusReadHoldingRegisters(plc_handle, L2Cmds[keys[i]].IDCType, L2Cmds[keys[i]].Addr, 125, L2Cmds[keys[i]].Data);
                        if (rs == 0)
                        {
                            UpdateRegDataBuff(L2Cmds[keys[i]].Data);
                        }
                        else
                        {
                            break;
                        }
                    }




                    int count = CmdQ.Count;
                    for (int i = 0; i < count; i++)
                    {
                        CommandUnit cu = null;
                        if (CmdQ.TryDequeue(out cu))
                        {
                            switch (cu.IDCType)
                            {
                                case "D"://写字
                                    int rs = ModbusSdk.ModbusWriteRegisters(plc_handle, cu.IDCType, cu.Addr, (ushort)cu.DataList.Count, cu.DataList.ToArray());
                                    if (rs == 0)
                                    {

                                    }
                                    else
                                    {
                                        break;
                                    }
                                    break;
                                case "R"://写位
                                    bool bt = ModbusSdk.ModbusWriteRElemBit(plc_handle, cu.Addr, cu.SubAddr, cu.BitValue);
                                    if (bt)
                                    {

                                    }
                                    else
                                    {
                                        break;
                                    }
                                    break;
                                default:
                                    break;
                            }


                        }
                    }

                }
                catch (Exception ex)
                {
                    OnErrorMsg?.Invoke("modbus connection lost : " + ex.Message);
                }







            }
        }

        #endregion====================================================================


        #region============================= 公开函数 ================================
        public CommHelper()
        {
            Init();

        }

        public void Start()
        {
            m_start = true;
            thr?.Start();
        }

        public void Stop()
        {
            m_start = false;
        }

        public void PushCmd(CommandUnit cmd)
        {

        }
        #endregion====================================================================


        #region============================= 消息响应 ================================
        #endregion====================================================================
    }
}
